
package checkersPlayer;

//import java.util.HashMap;

import checkersMain.CheckersBoard;
import checkersMain.CheckersPlayerInterface;
//
public class NegaScout implements CheckersPlayerInterface {
	public static final boolean DEBUG_MODE = true;

	public static final byte BOARD_SIZE = 8;
	public static final int BASIC_CHECKER_VALUE = 1;
	public static final int BASIC_KING_VALUE = 3;
	public static final int MINIMAX_SEARCH_DEPTH = 8;
	public static final byte MY_CHECKER = CheckersBoard.PLAYER1_CHECKER;
	public static final byte MY_KING = CheckersBoard.PLAYER1_KING;
	public static final byte HIS_CHECKER = CheckersBoard.PLAYER2_CHECKER;
	public static final byte HIS_KING = CheckersBoard.PLAYER2_KING;
	public static byte NUMBER_OF_ACTIVE_SQUARES = 32;

	public static void debugPrint(float foo) {
		if (NegaScout.DEBUG_MODE) {
			System.out.println(Float.toString(foo));
		}
	}

	public static void debugPrint(int foo) {
		if (NegaScout.DEBUG_MODE) {
			System.out.println(Integer.toString(foo));
		}
	}

	public static void debugPrint(Object foo) {
		if (NegaScout.DEBUG_MODE) {
			System.out.println(foo);
		}
	}

	//public HashMap<String, Float> scoreCache;

	public long HEUR_GEN_COUNT = 0;

	//public NegaScout() {
	//	scoreCache = new HashMap<String, Float>();
	//}
        protected int Turns;
        @Override
	public int choosePlyIndex(CheckersPlayerEvent cpe) {
		
                Turns = cpe.moveCount;
		double currentBest = -500;
		int bestBoard = 0;
		int numSuccessors = cpe.board.getNumSuccessors();
		if (numSuccessors == 1)
			return 0;
		for (int i = 0; i < cpe.board.getNumSuccessors(); i++) {
			double value = negamax(cpe.board.getSuccessor(i),MINIMAX_SEARCH_DEPTH);
			if (value >= currentBest) {
				bestBoard = i;
				currentBest = value;
			}
			 System.out.println("DONE negamax " +i+ " TIMES");
		}
		 System.out.println("IT FINISHED negamax");
		return bestBoard;
		
	}

	public void gameEnded(CheckersPlayerInterface.CheckersPlayerEvent cpe) {

	}

	public void gameStarted(CheckersPlayerInterface.CheckersPlayerEvent cpe) {
		
    }

	protected double generateSingleStateHeuristic(CheckersBoard board) {
            
                
		double heuristicValue = 0;
		for (int row = 0; row < 8; row++) {
			for (int col = 0; col < 8; col++) {
				byte contentsOfBoardSpace = board.getPiece(row, col);
				switch (contentsOfBoardSpace) {

				case CheckersBoard.PLAYER1_CHECKER:
					heuristicValue += MY_CHECKER;
					break;

				case CheckersBoard.PLAYER1_KING:
					heuristicValue += MY_KING;
					break;

				case CheckersBoard.PLAYER2_CHECKER:
					heuristicValue -= MY_CHECKER;
					break;

				case CheckersBoard.PLAYER2_KING:
					heuristicValue -= MY_KING;
					break;

				case CheckersBoard.OFFBOARD: // ignore the off-diagonal spaces
				case CheckersBoard.EMPTY: // ignore the empty spaces
					break;
				}
			}
		}
		if (Turns == 0) {
			byte BoardSpace1 = board.getPiece(4, 7);
			switch (BoardSpace1) {
			case CheckersBoard.PLAYER1_CHECKER:
				heuristicValue += MY_CHECKER * 3;
				break;
			}
			byte BoardSpace2 = board.getPiece(4, 3);
			switch (BoardSpace2) {
			case CheckersBoard.PLAYER1_CHECKER:
				heuristicValue += MY_CHECKER * 3;
				break;
			}
		}
		if (Turns == 1) {
			byte BoardSpace1 = board.getPiece(4, 7);
			switch (BoardSpace1) {
			case CheckersBoard.PLAYER1_CHECKER:
				heuristicValue += MY_CHECKER * 3;
				break;
			}
			byte BoardSpace2 = board.getPiece(4, 3);
			switch (BoardSpace2) {
			case CheckersBoard.PLAYER1_CHECKER:
				heuristicValue += MY_CHECKER * 3;
				break;
			}
		}
		if (Turns == 2) {
			byte BoardSpace1 = board.getPiece(4, 7);
			switch (BoardSpace1) {
			case CheckersBoard.PLAYER1_CHECKER:
				heuristicValue += MY_CHECKER * 3;
				break;
			}
			byte BoardSpace2 = board.getPiece(4, 3);
			switch (BoardSpace2) {
			case CheckersBoard.PLAYER1_CHECKER:
				heuristicValue += MY_CHECKER * 3;
				break;
			/*
			 * byte BoardSpace3 = theBoard.getSpaceRectangularPos(5,4);
			 * switch(BoardSpace3) { case CheckersBoard.PLAYER2_CHECKER:
			 * heuristicValue -= myCheckerValue6000; break; }
			 */
			}
		}
		double Center = getCenter(board, Turns);
		if (Turns > 40) {

			if (heuristicValue < -1) {
				byte BoardSpace1 = board.getPiece(0);
				switch (BoardSpace1) {
				case CheckersBoard.PLAYER1_KING:
					heuristicValue += MY_KING * 3;
					break;
				}
			}
			if (heuristicValue > 1) {
				{
					int i;
					for (i = 0; i < 32; i++) {
						byte BoardSpace1 = board.getPiece(i);
						switch (BoardSpace1) {
						case CheckersBoard.PLAYER1_CHECKER:
							heuristicValue += MY_CHECKER;
							break;

						case CheckersBoard.PLAYER1_KING:
							heuristicValue += MY_KING;
							break;

						case CheckersBoard.PLAYER2_CHECKER:
							heuristicValue -= MY_CHECKER * 2;
							break;

						case CheckersBoard.PLAYER2_KING:
							heuristicValue -= MY_KING * 2;
							break;

						case CheckersBoard.OFFBOARD: // ignore the off-diagonal
							// spaces
						case CheckersBoard.EMPTY: // ignore the empty spaces
							break;
						}
					}
					heuristicValue += 0.9 * Center;
				}
			}
		}
		double Edge = getEdge(board, Turns);
		double inverse = 1 / (Edge + Center);
		if (Turns <= 21 && Turns != 0 && Turns != 1) {
			return heuristicValue + 0.3 * inverse * Edge + 0.3 * inverse * Center;
		} else
			return heuristicValue;
	}

	
        
        public double getEdge(CheckersBoard theBoard, int turnCount) {
                System.out.println("getedge");
		float EdgeValueIn = 0;
		int col = 0;
		for (int row = 0; row < 8; row++) {
			byte contentsOfBoardSpace = theBoard.getPiece(row, col);
			switch (contentsOfBoardSpace) {

			case CheckersBoard.PLAYER1_CHECKER:
				EdgeValueIn += MY_CHECKER;
				break;

			case CheckersBoard.PLAYER1_KING:
				EdgeValueIn += MY_KING;
				break;

			case CheckersBoard.OFFBOARD: // ignore the off-diagonal spaces
			case CheckersBoard.EMPTY: // ignore the empty spaces
				break;
			}
		}
		int col2 = 7;
		for (int row = 0; row < 8; row++) {
			byte contentsOfBoardSpace = theBoard.getPiece(row, col2);
			switch (contentsOfBoardSpace) {

			case CheckersBoard.PLAYER1_CHECKER:
				EdgeValueIn += MY_CHECKER;
				break;

			case CheckersBoard.PLAYER1_KING:
				EdgeValueIn += MY_KING;
				break;

			case CheckersBoard.OFFBOARD: // ignore the off-diagonal spaces
			case CheckersBoard.EMPTY: // ignore the empty spaces
				break;
			}
		}
		double heuristicEdgeValue;
		heuristicEdgeValue = EdgeValueIn + 0.02 * turnCount;
		return heuristicEdgeValue;
	}
        public double getCenter(CheckersBoard theBoard, int turnCount) {
                System.out.println("getcenter");
		double CenterValueIn = 0;
		for (int row = 2; row < 6; row++) {
			for (int col = 2; col < 6; col++) {
				byte contentsOfBoardSpace = theBoard.getPiece(row, col);
				switch (contentsOfBoardSpace) {

				case CheckersBoard.PLAYER1_CHECKER:
					CenterValueIn += MY_CHECKER;
					break;

				case CheckersBoard.PLAYER1_KING:
					CenterValueIn += MY_KING;
					break;

				case CheckersBoard.OFFBOARD: // ignore the off-diagonal spaces
				case CheckersBoard.EMPTY: // ignore the empty spaces
					break;
				}
			}
                }
                        double heuristicCenterValue;
                         heuristicCenterValue = CenterValueIn + 2 - 0.01 * turnCount;
                        return heuristicCenterValue;

	
	}

	public String getDescription() {
		return "Checkers AI that uses the NegaScout algorithm instead of a "
				+ " standard minimax. We have also added our own heuristics"
				+ "\n\nAuthor: Brandon Benitez, Issa Katuga, David Lyle\nVersion: 1.00 - 15 October 2012";
	}

	public String getName() {
		return "negascout heuristic";
	}

	

	
	//protected String getStorableBoardString(CheckersBoard board) {
	//	String boardString = "";
	//	for (int i = 0; i < NUMBER_OF_ACTIVE_SQUARES; i++) {
	//		boardString = boardString.concat(Byte.toString(board.getPiece(i)));
	//	}

	//	return boardString;
	//}

	public double negamax(CheckersBoard board, int depth) {
		return negamax(board, depth, Float.MIN_VALUE, Float.MAX_VALUE, 1);
	}

	protected double negamax(CheckersBoard board, int depth, float alpha,
			float beta, int color) {

                
		if (depth == 0 || board.getNumSuccessors()==0) {
			alpha = (float) (color * generateSingleStateHeuristic(board));
                        
                        
			return alpha;
		} 
  
               // else if (scoreCache.containsKey(getStorableBoardString(board))) {
			//alpha  = scoreCache.get(getStorableBoardString(board));
			//return alpha;
		//} 
                    else {
                     float tempB = beta;
			for (int i = 0; i < board.getNumSuccessors(); i++) {
                                // call the Negamax search
                            
                            System.out.println(depth);
                                double score = -1 * (negamax(board.getSuccessor(i)
						.invertCheckersBoard(), depth - 1, -1 * tempB, -1
						* alpha, -1 * color));
                                
                                if (alpha < score && score < beta) {
                                   score = -1 * (negamax(board.getSuccessor(i).invertCheckersBoard(), depth - 1, -1 * beta, -1 * alpha, -1 * color ));
                                }
                                
                                alpha = (float) Math.max(alpha, score);
                                
                                if (alpha >= beta) {
                                    
                                    return alpha;
                                }
                                
                                tempB = alpha + 1;
                            
                                //  This seems to be a simplified version of the Alpha-Beta pruning section, if so, this must be expanded.                              
//				alpha = (float) Math.max(alpha, -1 * (negamax(board.getSuccessor(i)
//						.invertCheckersBoard(), depth - 1, -1 * beta, -1
//						* alpha, -1 * color)));
//
//				if (alpha > beta) {
//					break;
//				}
			}

			
			//if (!scoreCache.containsKey(getStorableBoardString(board))) {
                                
			//	scoreCache.put(getStorableBoardString(board), (float) alpha);
			//}
			
		}

		
		return alpha;
	}

	public void remainingTimeChanged(CheckersPlayerInterface.CheckersPlayerEvent cpe) {

	}
}
